/***************************************************************************************
 *
 *  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
 *
 *  By downloading, copying, installing or using the software you agree to this license.
 *  If you do not agree to this license, do not download, install, 
 *  copy or use the software.
 *
 *  Copyright (C) 2014-2019, Happytimesoft Corporation, all rights reserved.
 *
 *  Redistribution and use in binary forms, with or without modification, are permitted.
 *
 *  Unless required by applicable law or agreed to in writing, software distributed 
 *  under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 *  CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
 *  language governing permissions and limitations under the License.
 *
****************************************************************************************/

#include "sys_inc.h"
#include "sys_log.h"
#include "ihash.h"

/****************************************************************\
HASH�������ܺ����� ��ʼ������,Call-Id����ֵ���㣬���/ɾ��
\****************************************************************/
IHASHCTX * ihash_init(uint32 hash_num, uint32 link_num)
{
	IHASHCTX * p_ctx = (IHASHCTX *)malloc(sizeof(IHASHCTX));
	if (p_ctx == NULL)
	{
		return NULL;
	}

	memset(p_ctx, 0, sizeof(IHASHCTX));

	p_ctx->hash_num = hash_num;
	p_ctx->link_num = link_num;

	p_ctx->hash_array = (IHASHNODE *)malloc(sizeof(IHASHNODE) * hash_num);
	if (p_ctx->hash_array == NULL)
	{
		free(p_ctx);
		return NULL;
	}
	memset(p_ctx->hash_array, 0, sizeof(IHASHNODE) * hash_num);

	p_ctx->link_array = (IHASHNODE *)malloc(sizeof(IHASHNODE) * link_num);
	if (p_ctx->link_array == NULL)
	{
		free(p_ctx->hash_array);
		free(p_ctx);
		return NULL;
	}
	memset(p_ctx->link_array, 0, sizeof(IHASHNODE) * link_num);

	p_ctx->hash_semMutex  = sys_os_create_mutex();
	p_ctx->link_semMutex  = sys_os_create_mutex();

	uint32 i;
	for (i=1; i<p_ctx->link_num; i++)
	{
		p_ctx->link_array[i].next_index = i+1;
		if (i == (p_ctx->link_num - 1))
		{
			p_ctx->link_array[i].next_index = 0;
		}	
	}

	p_ctx->link_index = 1;	// ��һ����Ԫ0����

	return p_ctx;
}

void ihash_uninit(IHASHCTX * p_ctx)
{
    if (NULL == p_ctx)
    {
        return;
    }
    
    if (p_ctx->hash_array)
    {
        free(p_ctx->hash_array);
        p_ctx->hash_array = NULL;
    }

    if (p_ctx->link_array)
    {
        free(p_ctx->link_array);
        p_ctx->link_array = NULL;
    }

    if (p_ctx->hash_semMutex)
    {
        sys_os_destroy_sig_mutex(p_ctx->hash_semMutex);
        p_ctx->hash_semMutex = NULL;
    }

    if (p_ctx->link_semMutex)
    {
        sys_os_destroy_sig_mutex(p_ctx->link_semMutex);
        p_ctx->link_semMutex = NULL;
    }

    free(p_ctx);
}

uint32 ihash_link_pop(IHASHCTX * p_ctx)
{
	sys_os_mutex_enter(p_ctx->link_semMutex);

	uint32 ret_index = p_ctx->link_index;
	p_ctx->link_index = p_ctx->link_array[ret_index].next_index;

	p_ctx->link_array[ret_index].bFreeList = 0;

	sys_os_mutex_leave(p_ctx->link_semMutex);

	return ret_index;
}

void ihash_link_push(IHASHCTX * p_ctx, uint32 push_index)
{
	if (push_index == 0)
	{
        ErrorLog( "%s, push_index == 0!!!\r\n", __FUNCTION__);
		log_print(LOG_ERR,"----------%s[%d]--------------\n",__FILE__,__LINE__);
		return;
	}

	if (push_index >= p_ctx->link_num)
	{
        ErrorLog( "%s, push_index[%u] >= MAX_NUM[%u]!!!\r\n", __FUNCTION__, push_index, p_ctx->link_num);
		log_print(LOG_ERR,"----------%s[%d]--------------\n",__FILE__,__LINE__);
        return;
	}

	if (p_ctx->link_array[push_index].bFreeList == 1)
	{
        ErrorLog("%s, push_index[%u] bFreeList == 1!!!\r\n", __FUNCTION__, push_index);
		log_print(LOG_ERR,"----------%s[%d]--------------\n",__FILE__,__LINE__);
        return;
	}

	sys_os_mutex_enter(p_ctx->link_semMutex);

	memset(&p_ctx->link_array[push_index],0,sizeof(IHASHNODE));
	p_ctx->link_array[push_index].bFreeList = 1;

	p_ctx->link_array[push_index].next_index = p_ctx->link_index;
	p_ctx->link_index = push_index;

	sys_os_mutex_leave(p_ctx->link_semMutex);
}

uint32 ihash_index(IHASHCTX * p_ctx, const char * key_str)
{
	int i, len = strlen(key_str);

	register uint32 nr = 1, nr2 = 4;
	register uint8 ctmp;
	
	for (i=0; i<len; i++)
	{
		ctmp = key_str[i];
		nr^= (((nr & 63)+nr2)*((uint32)ctmp))+ (nr << 8);
		nr2+=3;
	}

	return(nr & (p_ctx->hash_num -1));
}

BOOL ihash_add(IHASHCTX * p_ctx, const char * key_str, uint64 index, int type)
{
	if (p_ctx == NULL)
	{
		return FALSE;
	}
	
	uint32 hash_key = ihash_index(p_ctx,key_str);

	sys_os_mutex_enter(p_ctx->hash_semMutex);

	if (p_ctx->hash_array[hash_key].bNodeValidate == 0)
	{
		strncpy(p_ctx->hash_array[hash_key].key_str, key_str, IHASH_KEY_MAX_LEN);
		p_ctx->hash_array[hash_key].index = index;
		p_ctx->hash_array[hash_key].next_index = 0;
		p_ctx->hash_array[hash_key].bLinkValidate = 0;
		p_ctx->hash_array[hash_key].bNodeValidate = 1;
	}
	else
	{
		IHASHNODE * p_node = &p_ctx->hash_array[hash_key];

		if (type == 0)
		{
			while ((p_node->bLinkValidate == 1) && (strcmp(key_str,p_node->key_str) != 0))
			{
				p_node = &p_ctx->link_array[p_node->next_index];
				if ((p_node - p_ctx->link_array) > (int)p_ctx->link_num)
				{
					sys_os_mutex_leave(p_ctx->hash_semMutex);
					log_print(LOG_ERR,"----------%s[%d]--------------\n",__FILE__,__LINE__);
                    ErrorLog( "%s, link node address = 0x%x\r\n", __FUNCTION__, p_node);
					return FALSE;
				}
			}

			if (strcmp(key_str,p_node->key_str) == 0)
			{
				if (p_node->index == index)
				{
					// ��ȫ��ͬ���ظ����
				}
				else
				{
					p_node->index = index;
				}
			}
			else
			{
				uint32 link_index = ihash_link_pop(p_ctx);
				if (link_index == 0)
				{
					sys_os_mutex_leave(p_ctx->hash_semMutex);
					log_print(LOG_ERR,"----------%s[%d]--------------\n",__FILE__,__LINE__);
                    ErrorLog( "%s, no free node!!!\r\n", __FUNCTION__);
					return FALSE;
				}
	
				strncpy(p_ctx->link_array[link_index].key_str, key_str, IHASH_KEY_MAX_LEN);
				p_ctx->link_array[link_index].index = index;
				p_ctx->link_array[link_index].next_index = 0;
				p_ctx->link_array[link_index].bLinkValidate = 0;
				p_ctx->link_array[link_index].bNodeValidate = 1;
	
				p_node->next_index = link_index;
				p_node->bLinkValidate = 1;
			}
		}
		else 
		{
			while ((p_node->bLinkValidate == 1) && ((strcmp(key_str,p_node->key_str) != 0) || p_node->index != index))
			{
				p_node = &p_ctx->link_array[p_node->next_index];
				if ((p_node - p_ctx->link_array) > (int)p_ctx->link_num)
				{
					sys_os_mutex_leave(p_ctx->hash_semMutex);
					log_print(LOG_ERR,"----------%s[%d]--------------\n",__FILE__,__LINE__);
                    ErrorLog( "%s, link node address = 0x%x\r\n", __FUNCTION__, p_node);
					return FALSE;
				}
			}

			if ((strcmp(key_str,p_node->key_str) == 0) && p_node->index == index)
			{
				// ��ȫ��ͬ���ظ����
			}
			else
			{
				uint32 link_index = ihash_link_pop(p_ctx);
				if (link_index == 0)
				{
					sys_os_mutex_leave(p_ctx->hash_semMutex);
					log_print(LOG_ERR,"----------%s[%d]--------------\n",__FILE__,__LINE__);
                    ErrorLog( "%s, no free node!!!\r\n", __FUNCTION__);
					return FALSE;
				}
	
				strncpy(p_ctx->link_array[link_index].key_str, key_str, IHASH_KEY_MAX_LEN);
				p_ctx->link_array[link_index].index = index;
				p_ctx->link_array[link_index].next_index = 0;
				p_ctx->link_array[link_index].bLinkValidate = 0;
				p_ctx->link_array[link_index].bNodeValidate = 1;

				p_node->next_index = link_index;
				p_node->bLinkValidate = 1;
			}
		}
	}

	sys_os_mutex_leave(p_ctx->hash_semMutex);

	return TRUE;
}

BOOL ihash_del(IHASHCTX * p_ctx, const char * key_str, uint64 index)
{
	if (p_ctx == NULL)
	{
		return FALSE;
	}
	
	uint32 hash_key = ihash_index(p_ctx,key_str);

	sys_os_mutex_enter(p_ctx->hash_semMutex);

	if (p_ctx->hash_array[hash_key].bNodeValidate == 0)
	{
		sys_os_mutex_leave(p_ctx->hash_semMutex);
		return FALSE;
	}

	IHASHNODE * p_node = &p_ctx->hash_array[hash_key];

	if ((strcmp(key_str,p_node->key_str) == 0) && (p_node->index == index))
	{
		p_ctx->hash_array[hash_key].bNodeValidate = 0;

		if (p_ctx->hash_array[hash_key].bLinkValidate == 1)
		{
			uint32 del_link_index = p_ctx->hash_array[hash_key].next_index;
			IHASHNODE * p_del_node = &p_ctx->link_array[del_link_index];

			memcpy(p_node,p_del_node,sizeof(IHASHNODE));

			ihash_link_push(p_ctx,del_link_index);
		}

		sys_os_mutex_leave(p_ctx->hash_semMutex);
		return TRUE;
	}
	else
	{
		IHASHNODE * p_node_prev = p_node;

		while (p_node_prev->bLinkValidate == 1)
		{
			p_node = &p_ctx->link_array[p_node_prev->next_index];

			if ((p_node - p_ctx->link_array) > (int)p_ctx->link_num)
			{
				sys_os_mutex_leave(p_ctx->hash_semMutex);
				log_print(LOG_ERR,"----------%s[%d]--------------\n",__FILE__,__LINE__);
                ErrorLog( "%s, link node address = 0x%x\r\n", __FUNCTION__, p_node);
				return FALSE;
			}

			if ((strcmp(key_str,p_node->key_str) == 0) && (p_node->index == index))
			{
				p_node->bNodeValidate = 0;

				p_node_prev->next_index = p_node->next_index;
				p_node_prev->bLinkValidate = p_node->bLinkValidate;

				uint32 del_link_index = p_node - p_ctx->link_array;
				ihash_link_push(p_ctx,del_link_index);

				break;
			}

			p_node_prev = p_node;
		}
	}

	sys_os_mutex_leave(p_ctx->hash_semMutex);

	return TRUE;
}

uint64 ihash_find_index_from_keystr(IHASHCTX * p_ctx, const char * key_str)
{
	if (p_ctx == NULL)
	{
		return (uint64)-1;
	}
	
	uint32 hash_key = ihash_index(p_ctx,key_str);
	IHASHNODE * p_node = &p_ctx->hash_array[hash_key];

	sys_os_mutex_enter(p_ctx->hash_semMutex);

	while (p_node->bNodeValidate == 1)
	{
		if (strcmp(key_str,p_node->key_str) == 0)
		{
			sys_os_mutex_leave(p_ctx->hash_semMutex);
			return p_node->index;
		}

		if (p_node->bLinkValidate == 1)
		{
			if (p_node->next_index < p_ctx->link_num)
			{
				p_node = &p_ctx->link_array[p_node->next_index];
			}				
			else
			{
				break;
			}				
		}
		else
		{
			break;
		}				
	}

	sys_os_mutex_leave(p_ctx->hash_semMutex);

	return (uint64)-1;
}

void ihash_lock(IHASHCTX * p_ctx)
{
	if (p_ctx != NULL)
	{
		sys_os_mutex_enter(p_ctx->hash_semMutex);
	}		
}

void ihash_unlock(IHASHCTX * p_ctx)
{
	if (p_ctx != NULL)
	{
		sys_os_mutex_leave(p_ctx->hash_semMutex);
	}		
}

void * ihash_save_var(IHASHCTX * p_ctx)
{
	if (p_ctx == NULL)
	{
		return NULL;
	}
	
	return p_ctx->p_node;
}

void ihash_restore_var(IHASHCTX * p_ctx, void * p_node)
{
	if (p_ctx == NULL)
	{
		return;
	}
	
	p_ctx->p_node = (IHASHNODE *)p_node; // �˴�Ӧ�ü��ָ���Ƿ�����Ч��Χ�ڲ����뵽����
}



